
<template>
  <div
    :class="'vue-scrollbar__wrapper' + ( this.classes ? ' ' + this.classes : '' )"
    ref="scrollWrapper"
    :style="this.styles">

    <div
      class="vue-scrollbar__area vue-scrollbar-transition"
      ref="scrollArea"
      @wheel="scroll"
      :style="{
        marginTop: this.top * -1 +'px',
        marginLeft: this.left * -1 +'px'
      }">

        <slot></slot>

        <div
        v-if="scrollbarVer.height < 100"
        class="vue-scrollbar__scrollbar-vertical"
        ref="scrollbarVerArea"
        @click="jumpVertical">

            <div
            class="scrollbar vue-scrollbar-transition"
            ref="scrollbarVer"
            draggable="true"
            :style="{
                height: scrollbarVer.height + '%',
                top: scrollbarVer.scrolling + '%'
            }">
            </div>

        </div>

        <div
        v-if="scrollbarHor.width < 100"
        class="vue-scrollbar__scrollbar-horizontal"
        ref="scrollbarHorArea"
        @click="jumpHorizontal">

            <div
            class="scrollbar vue-scrollbar-transition"
            ref="scrollbarHor"
            :style="{
                width:scrollbarHor.width + '%',
                left:scrollbarHor.scrolling + '%'
            }" >
            </div>

        </div>

    </div>

  </div>

</template>


<script>

  export default {
      name: 'Scrollbar',
      props: {
          classes: String,
          styles: Object,
          speed: {
              type: Number,
              default: 53
          }
      },

      data () {
          return  {
              ready: false,
              top: 0,
              left: 0,
              scrollAreaHeight: null,
              scrollAreaWidth: null,
              scrollWrapperHeight: null,
              scrollWrapperWidth: null,
              vMovement: 0,
              hMovement: 0,
              scrollbarVer:{
                  dragging: false,
                  height:0,
                  scrolling:0,
                  start:0
              },
              scrollbarHor:{
                  dragging: false,
                  width:0,
                  scrolling:0,
                  start:0
              },
              dragging: false,
          }
      },

      mounted () {
          this.$nextTick(function () {
              let scrollbarHor = this.$refs.scrollbarHor
              let scrollbarVer = this.$refs.scrollbarVer

              // 添加window监听
              this.bindEvent(window, 'resize', this.calculateSize)

              //添加滚动内容变化监听
              let MutationObserver = window.MutationObserver ||
                  window.WebKitMutationObserver ||
                  window.MozMutationObserver;
              let mutationObserverSupport = !!MutationObserver;
              if(mutationObserverSupport){
                  let observer = new MutationObserver(this.resetY);
                  observer.observe(this.$refs.scrollArea, {
                      childList: true,
                      subtree:true
                  });
              } else{
                  this.bindEvent(this.$refs.scrollArea, 'DOMNodeInserted', this.resetY)
              }

              // 添加纵向滚动条监听事件
              this.bindEvent(scrollbarVer, 'mousedown', this.dragVertical)
              this.bindEvent(scrollbarVer, 'mousemove', this.dragVertical)
              this.bindEvent(scrollbarVer, 'mouseup', this.dragVertical)
              // 添加横向滚动条监听事件
              this.bindEvent(scrollbarHor, 'mousedown', this.dragHorizontal)
              this.bindEvent(scrollbarHor, 'mousemove', this.dragHorizontal)
              this.bindEvent(scrollbarHor, 'mouseup', this.dragHorizontal)
          })
      },

      methods: {
          bindEvent(element, type, func) { //事件绑定方法
              if (element.addEventListener) {
                  element.addEventListener(type, func, false);
              } else if (element.attachEvent) {
                  element.attachEvent("on" + type, func);
              } else {
                  element["on" + type] = func;
              }
          },

          removeEvent(element, type, handler){
              if (element.removeEventListener){
                  element.removeEventListener(type, handler, false);
              } else if (element.detachEvent){
                  element.detachEvent("on" + type, handler);
              } else {
                  element["on" + type] = null;
              }
          },

          scroll(e){
              e.preventDefault() //阻止默认事件
              e.stopPropagation() //停止事件传播

              // Make sure the content height is not changed
              this.calculateSize(() => {
                  // Set the wheel step
                  let num = this.speed

                  // DOM events
                  let shifted = e.shiftKey
                  let scrollY = e.deltaY > 0 ? num : -(num)
                  let scrollX = e.deltaX > 0 ? num : -(num)

                  // Fix Mozilla Shifted Wheel~
                  if(shifted && e.deltaX == 0) scrollX = e.deltaY > 0 ? num : -(num)

                  // Next Value
                  let nextY = this.top + scrollY
                  let nextX = this.left + scrollX

                  // Is it Scrollable?
                  let canScrollY = this.scrollAreaHeight > this.scrollWrapperHeight
                  let canScrollX = this.scrollAreaWidth > this.scrollWrapperWidth

                  // Vertical Scrolling
                  if(canScrollY && !shifted) this.normalizeVertical(nextY)

                  // Horizontal Scrolling
                  if(shifted && canScrollX) this.normalizeHorizontal(nextX)
              })

          },

          scrollToY(y) {
              this.normalizeVertical(y)
          },

          scrollToX(x) {
              this.normalizeVertical(x)
          },

          normalizeVertical(next){
              let elementSize = this.getSize()

              // Vertical Scrolling
              let lowerEnd = elementSize.scrollAreaHeight - elementSize.scrollWrapperHeight

              // Max Scroll Down
              if(next > lowerEnd && lowerEnd > 0) next = lowerEnd

              // Max Scroll Up
              else if(next < 0) next = 0

              // Update the Vertical Value
              this.top = next,
              this.scrollbarVer.scrolling = next / elementSize.scrollAreaHeight * 100
          },

          normalizeHorizontal(next){
              let elementSize = this.getSize()

              // 横向滚动
              let rightEnd = elementSize.scrollAreaWidth - this.scrollWrapperWidth

              // 最大滚动
              if(next > rightEnd && rightEnd > 0) next = rightEnd;

              // 最小滚动
              else if(next < 0) next = 0

              // 更新滚动
              this.left = next,
              this.scrollbarHor.scrolling = next / elementSize.scrollAreaWidth * 100
          },

          changePosition(movement, orientation){
              // 检测滚动区域是否变化
              this.calculateSize(() => {
                  // Convert Percentage to Pixel
                  let next = movement / 100
                  if( orientation == 'vertical' ) this.normalizeVertical( next * this.scrollAreaHeight )
                  if( orientation == 'horizontal' ) this.normalizeHorizontal( next * this.scrollAreaWidth )
              })
          },

          dragHorizontal(e){
              e.preventDefault() //阻止默认事件
              e.stopPropagation() //停止事件传播
//              e = e.changedTouches ? e.changedTouches[0] : e //是否为触摸事件

              switch (e.type) {//判断事件类型
                  case "mousedown":
                      this.scrollbarHor.dragging = true;
                      this.scrollbarHor.start = e.clientX
                      break;
                  case "mousemove":
                      if(this.scrollbarHor.dragging){
                          // 计算移动百分比
                          let movement = e.clientX - this.scrollbarHor.start
                          let movementPercentage = movement / this.scrollWrapperWidth * 100

                          // 移动后所在的位置
                          let next = this.scrollbarHor.scrolling + movementPercentage

                          // 改变位置
                          this.changePosition(next, 'horizontal')
                      }
                      break;
                  case "mouseup":
                      // 清空拖动
                      this.scrollbarHor.dragging = false
                      break;
              }
          },

          dragVertical(e){
              e.preventDefault() //阻止默认事件
              e.stopPropagation() //停止事件传播
//              e = e.changedTouches ? e.changedTouches[0] : e //是否为触摸事件

              switch (e.type) {//判断事件类型
                  case "mousedown":
                      this.scrollbarVer.dragging = true;
                      this.scrollbarVer.start = e.clientY
                      break;
                  case "mousemove":
                      if(this.scrollbarVer.dragging){
                          // 计算移动百分比
                          let movement = e.clientY - this.scrollbarVer.start
                          let movementPercentage = movement / this.scrollWrapperHeight * 100

                          // 移动后所在的位置
                          let next = this.scrollbarVer.scrolling + movementPercentage

                          // 改变位置
                          this.changePosition(next, 'vertical')
                      }
                      break;
                  case "mouseup":
                      this.scrollbarVer.dragging = false
                      break;
              }
          },

          jumpHorizontal(e){

              let isContainer = e.target === this.$refs.scrollbarHorArea

              if(isContainer){

                  // 获取滚动条位置
                  let position = this.$refs.scrollbarHor.getBoundingClientRect()

                  // 计算移动百分比
                  let movement = e.clientX - position.left
                  if(e.clientX > position.right){
                      movement = e.clientX - position.right
                  }
                  let movementPercentage = movement / this.scrollWrapperWidth * 100

                  // 移动后所在的位置
                  let next = this.scrollbarHor.scrolling + movementPercentage

                  // 改变位置
                  this.changePosition(next, 'horizontal')
              }
          },

          jumpVertical(e){

              let isContainer = e.target === this.$refs.scrollbarVerArea

              if(isContainer){

                  // 获取滚动条位置
                  let position = this.$refs.scrollbarVer.getBoundingClientRect()

                  // 计算竖向移动百分比
                  let movement = e.clientY - position.top
                  if(e.clientY > position.bottom){
                      movement = e.clientY - position.bottom
                  }
                  let movementPercentage = movement / this.scrollWrapperHeight * 100

                  // 移动后所在的位置
                  let next = this.scrollbarVer.scrolling + movementPercentage

                  // 改变位置
                  this.changePosition(next, 'vertical')

              }
          },

          getSize(){
              // The Elements
              let $scrollArea = this.$refs.scrollArea
              let $scrollWrapper = this.$refs.scrollWrapper

              if(!$scrollArea && !$scrollWrapper) return false

              // Get new Elements Size
              let elementSize = {
                  // Scroll Area Height and Width
                  scrollAreaHeight: $scrollArea.scrollHeight,
                  scrollAreaWidth: $scrollArea.scrollWidth,

                  // Scroll Wrapper Height and Width
                  scrollWrapperHeight: $scrollWrapper.offsetHeight,
                  scrollWrapperWidth: $scrollWrapper.offsetWidth,
              }
              return elementSize
          },

          calculateSize(cb){
              if(typeof cb !== 'function') cb = null;

              let elementSize = this.getSize()

              if( elementSize.scrollWrapperHeight !== this.scrollWrapperHeight ||
                  elementSize.scrollWrapperWidth !== this.scrollWrapperWidth ||
                  elementSize.scrollAreaHeight !== this.scrollAreaHeight ||
                  elementSize.scrollAreaWidth !== this.scrollAreaWidth )
              {

                  // 滚动区域宽高
                  this.scrollAreaHeight = elementSize.scrollAreaHeight;
                  this.scrollAreaWidth = elementSize.scrollAreaWidth;

                  // 滚动器宽高
                  this.scrollWrapperHeight = elementSize.scrollWrapperHeight;
                  this.scrollWrapperWidth = elementSize.scrollWrapperWidth;

                  // 计算滚动条大小
                  this.scrollbarHor.width = elementSize.scrollWrapperWidth / elementSize.scrollAreaWidth * 100;
                  this.scrollbarVer.height = elementSize.scrollWrapperHeight / elementSize.scrollAreaHeight * 100

                  this.ready = true

                  return cb ? cb() : false
              }

              else return cb ? cb() : false
          },
          resetX(){
              this.changePosition(0, 'horizontal')
          },
          resetY(mutations){
              let elementSize = this.getSize()
              if(elementSize && elementSize.scrollAreaHeight != this.scrollAreaHeight ){
                  this.changePosition(0, 'vertical')
              }

          }


      },

      beforeDestroy (){
      }

  }

</script>
